package tetris;

import tetris.simulator.*;
import tetris.agent.*;
import tetris.agent.Agent.feature;

public class Main
{

        public static void main(String[] args)
        {
                //RunDefaultAgent();
                RunCEM();
                //RunHillClimber();
        }
        
        ////
        // Shows the performance of the default agent's controller.
        ////
        static void RunDefaultAgent()
        {
              State sFinal = runGraphics();
              System.out.println("You have completed " + sFinal.getRowsCleared() + " rows.");
        }
        
        
        ////
        // Optimizes parameters with a simple, dumb stochastic hill climber.
        ////
        static void RunHillClimber()
        {
        	int dim = feature.values().length;
            double[] theta = new double[dim];

            
			theta[feature.OVERHANG.ordinal()] =102.484270 ;
			theta[feature.LINES_CLEARED.ordinal()] =- 15.799548;
			theta[feature.MAX_HEIGHT.ordinal()] = -1.962944;
			theta[feature.MIN_HEIGHT.ordinal()] = -2.279447;
			theta[feature.NUM_SQUARES.ordinal()] = 3.409913;
			theta[feature.DIFFS.ordinal()] =  12.931093;
			theta[feature.ISOLATION.ordinal()] = 24.722818;
			theta[feature.HOLES.ordinal()] = 19.323345;
			
        	HillClimber climber = new HillClimber(theta);
        	
        	Agent best = climber.hillClimb(new State(), 100000);
        	
            System.out.printf("Best theta: ");
            for(int i = 0; i < best.theta.length; i++)
            {
                    System.out.printf("%s %f\n", Agent.feature.values()[i].toString(), best.theta[i]);
            }
            
            
            runGraphics(best);
        	
        	
        }
        
        
        ////
        // Optimizes parameters with the cross entropy method
        ////
        static void RunCEM()
        {
                int dim = feature.values().length;
                double[] theta = new double[dim];

                theta[feature.OVERHANG.ordinal()] = 0.0;
                theta[feature.LINES_CLEARED.ordinal()] =0.0;
                theta[feature.MAX_HEIGHT.ordinal()] = 0.0;
                theta[feature.MIN_HEIGHT.ordinal()] = 0.0;
                theta[feature.NUM_SQUARES.ordinal()] = 0.0;
                theta[feature.DIFFS.ordinal()] =  0.0;
                theta[feature.ISOLATION.ordinal()] = 0.0;
                theta[feature.HOLES.ordinal()] = 0.0;
                
                double[][] initialCovariance = new double[dim][dim];
                for(int i = 0; i < dim; i++)
                {
                        initialCovariance[i][i] = 100;
                }
                
                AgentEvolver evolver = new AgentEvolver(100, 10, theta, initialCovariance);
                
                evolver.evolveAgents(new State(), 100);
                
                Agent best = evolver.getAgents().get(0);
                
                best.theta = evolver.best;
                System.out.printf("Best theta: ");
                for(int i = 0; i < best.theta.length; i++)
                {
                        System.out.printf("%s %f\n", Agent.feature.values()[i].toString(), best.theta[i]);
                }
                
                
                runGraphics(best);
                
        }

        // run the tetris game and save image of the board at each turn, returns
        // the final state
        public static State recordVideo()
        {
                int delay = 500;

                State s = new State();
                Visualizer v = new Visualizer(s);
                Agent a = new Agent();
                while (!s.hasLost())
                {
                        s.makeMove(a.chooseAction(s, s.legalMoves()));
                        v.draw();
                        v.drawNext(0, 0);
                        v.save(s.getTurnNumber() + ".png");
                        try
                        {
                                Thread.sleep(delay);
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                }
                v.dispose();
                return s;
        }

        // runs the tetris game until the game is over and returns the final
        // state
        public static State run()
        {
                State s = new State();
                Agent a = new Agent();
                while (!s.hasLost())
                {
                        s.makeMove(a.chooseAction(s, s.legalMoves()));
                }
                return s;
        }

        
        // runs and displays the tetris game until the game is over and returns
        // the final state
        public static State runGraphics(Agent a)
        {
                int delay = 500;

                State s = new State();
                Visualizer v = new Visualizer(s);
                while (!s.hasLost())
                {
                        s.makeMove(a.chooseAction(s, s.legalMoves()));
                        v.draw();
                        v.drawNext(0, 0);
                        try
                        {
                                Thread.sleep(delay);
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                }
                v.dispose();
                return s;
        }
        
        // runs and displays the tetris game until the game is over and returns
        // the final state
        public static State runGraphics()
        {
                int delay = 0;

                State s = new State();
                Visualizer v = new Visualizer(s);
                Agent a = new Agent();
                
                int k = 0;
                while (!s.hasLost())
                {
                        s.makeMove(a.chooseAction(s, s.legalMoves()));
                        
                        if(k % 100 == 0)
                        {
                                v.draw();
                                v.drawNext(0, 0);
                        }
                        try
                        {
                                Thread.sleep(delay);
                        }
                        catch (InterruptedException e)
                        {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                        
                        k++;
                        /*
                        try
                        {
                                Thread.sleep(delay);
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                        */
                }
                v.dispose();
                return s;
        }

        // allows a human player to play using the 4 arrow keys
        // left: move the piece left
        // right: move the piece right
        // top: change orientation
        // down: drop piece
        // there is no time limit for choosing where to place the next piece
        public static State runHumanPlayer()
        {
                int delay = 100;
                State s = new State();
                Visualizer v = new Visualizer(s);
                v.draw();
                v.drawNext(0, 0);
                while (!s.hasLost())
                {
                        try
                        {
                                Thread.sleep(delay);
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                }
                v.dispose();
                return s;
        }
}
